#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <syserr.h>
#include <device.h>
#include <utils.h>
#include <sys/epoll.h>
#include "printk.h"
#include "dev/rawdev.h"

#define to_raw_dev(dev) container_of(dev, struct raw_device, dev);

static int raw_open(device_t *dev)
{
    struct raw_device *raw = to_raw_dev(dev);

    int fd = raw->cfg.fd;

    ev__io_init(&dev->io, device_io, fd);
    ev__io_start(dev_loop(dev), &dev->io, EPOLLIN);
    dev_status(dev) = DEVICE_STATUS_CONNECTED;
    return fd;
}

static int raw_on_config_changed(struct raw_device *raw,
                                 const struct raw_dev_cfg *new)
{
    if (!memcmp(&raw->cfg, new, sizeof(struct raw_dev_cfg)))
        return 0;

    if (ev__is_active(&raw->dev))
    {
        if (raw->cfg.fd != new->fd)
            return -EIO;
    }

    memcpy(&raw->cfg, new, sizeof(struct raw_dev_cfg));
    return 0;
}
static int raw_ctrl(device_t *dev, u32 cmd, void *args)
{
    struct raw_device *raw = to_raw_dev(dev);

    switch (cmd)
    {
    case DEV_CTRL_CMD_GET_CFG:
        memcpy(args, &raw->cfg, sizeof(struct raw_dev_cfg));
        break;
    case DEV_CTRL_CMD_SET_CFG:
        return raw_on_config_changed(raw, args);
    default:
        return -ENOSYS;
    }
    return 0;
}

static const struct device_ops raw_ops =
{
    .open  = raw_open,
    .ctrl  = raw_ctrl,
};
static const struct raw_dev_cfg raw_dev_cfg_def =
{
    .fd       = -1,
};
int raw_dev_add(const char *name, const struct raw_dev_cfg *cfg)
{
    int ret;
    struct raw_device *raw = kcalloc(1, sizeof(struct raw_device));

    if (!raw)
        return -ENOMEM;

    if (!cfg) cfg = &raw_dev_cfg_def;
    memcpy(&raw->cfg, cfg, sizeof(struct raw_dev_cfg));

    raw->dev.ops = &raw_ops;

    ret = device_register(&raw->dev, name, 0);
    if (ret < 0)
        kfree(raw);

    return ret;
}
void raw_dev_del(device_t *dev)
{
    struct raw_device *raw = to_raw_dev(dev);

    device_unregister(&raw->dev);
    kfree(raw);
}
